﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartRoute.Analytics;
using SmartRoute.Data;

namespace SmartRoute.Optimization
{
    /// <summary>
    /// A typeclass for things that can be searched locally.
    /// </summary>
    public interface ILocallySearchable<T>
    {
        /// <summary>
        /// Returns the immediate neighbors of the given element.
        /// </summary>
        IEnumerable<T> NeighborSearch();

        /// <summary>Returns the neighbors up to <paramref name="depth"/> steps away from this example.</summary>
        /// <remarks>
        /// Callers should always use this instead of <see cref="ILocallySearchableFunctions.DefaultDeepLocalSearch`1"/>.
        /// <list type="bullet">
        /// <listheader>Notes for implementers:</listheader>
        /// <term>A default implementation, <see cref="ILocallySearchableFunctions.DefaultDeepLocalSearch`1"/>, is provided, feel free to simply wrap it</term>
        /// <term>If you have a more efficient implementation than the default, override this so that the caller sees this instead.</term>
        /// <term>Your implementation should probably also be IEquatable, although this is not required.</term>
        /// </list>
        /// </remarks>
        /// <param name="depth">The amount of mutation steps the local search will traverse over the space.</param>
        /// <returns>A list of all neighbors up to <paramref name="depth"/> steps away</returns>
        IEnumerable<T> DeepLocalSearch(uint depth);
    }

    public static class ILocallySearchableFunctions
    {
        /// <summary>
        /// A default implementation of a deep local search. Callers should use <see cref="ILocallySearchable.DeepLocalSearch`1"/> instead.
        /// </summary>
        /// <param name="subjectOfSearch">The locally searchable thing to be searched.</param>
        /// <param name="depth"></param>
        /// <returns></returns>
        public static IEnumerable<T> DefaultDeepLocalSearch<T>(T subjectOfSearch, uint depth) where T: ILocallySearchable<T>
        {
            return depth == 1 ? subjectOfSearch.NeighborSearch() :
                subjectOfSearch.NeighborSearch().SelectMany((x => DefaultDeepLocalSearch(x, depth - 1))).Distinct();
        }

        public static bool ConfirmNoBetterLocal<T, U>(uint buses, Location depot, IRoutingService routeService, 
            Analyzer<U> fitnessTest, T toTest, uint searchDepth) where T : ILocallySearchable<T>, ISolution<T>
        {
            var scoreHere = ISolutionFunctions.TestWith(buses, depot, routeService, fitnessTest, toTest);
            return toTest.DeepLocalSearch(searchDepth).All(x => scoreHere >= ISolutionFunctions.TestWith(buses, depot, routeService, fitnessTest, x));
        }
    }
}
